package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.threadlocal.WmThreadLocalUtils;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmNewsService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Transactional
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Value("${file.oss.web-site}")
    private String webSite;

    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;
    @Autowired
    private WmMaterialMapper wmMaterialMapper;

    /**
     * 分页查询自媒体文章列表
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult list(WmNewsPageReqDto dto) {
        //1.检查参数
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        dto.checkParam();

        //2.判断用户是否登陆
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        //3.判断dto每个属性值是否为空，如果不为空，就拼接查询条件
        LambdaQueryWrapper<WmNews> wrapper = new LambdaQueryWrapper<>();
        //判断状态值
        if (dto.getStatus() != null) {
            wrapper.eq(WmNews::getStatus, dto.getStatus());
        }

        //判断发布时间的起止时间
        if (dto.getBeginPubDate() != null && dto.getEndPubDate() != null) {
            wrapper.between(WmNews::getPublishTime, dto.getBeginPubDate(), dto.getEndPubDate());
        }

        //判断频道ID
        if (dto.getChannelId() != null && dto.getChannelId() > 0) {
            wrapper.eq(WmNews::getChannelId, dto.getChannelId());
        }

        //判断关键词
        if (StringUtils.isNotBlank(dto.getKeyword())) {
            wrapper.like(WmNews::getTitle, dto.getKeyword());
        }

        //4.查询条件添加 当前登陆用户ID
        wrapper.eq(WmNews::getUserId, user.getId());

        //5.查询条件指定排序方式，按照创建时间倒排
        wrapper.orderByDesc(WmNews::getCreatedTime);

        //6.执行分页查询，得到结果
        IPage<WmNews> page = new Page<>(dto.getPage(), dto.getSize());
        this.page(page, wrapper);

        //7.封装响应结果
        ResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), (int) page.getTotal());
        responseResult.setData(page.getRecords());
        responseResult.setHost(webSite); //设置素材的域名前缀

        return responseResult;
    }

    /**
     * 提交审核或保存草稿
     *
     * @param dto
     * @param isSubmit 0-表示保存草稿， 1-提交审核
     * @return
     */
    @Override
    public ResponseResult submit(WmNewsDto dto, Short isSubmit) {
        //1.第一部分：准入校验
        //1.1 校验参数
        if (dto == null || isSubmit == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //1.2 判断用户是否登录
        WmUser user = WmThreadLocalUtils.getUser();
        if (user == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        //2.第二部分：处理文章的保存或者更新

        //2.1拷贝属性
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(dto, wmNews);

        //2.2 判断当前布局方式
        //如果当前页面选择的布局方式是自动，那么保存时设置为null(自动布局前端传-1 ,数据库只有0 1 3)
        if (dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
            wmNews.setType(null);
        }

        //2.3 处理封面图片的值
        /**
         * 由于页面的封面图片请求进来时值的形式是JSON数组，
         格式: ["http://xxx.aliyn.com/a/b.jpg", "http://xxx.aliyn.com/a/b.jpg", "http://xxx.aliyn.com/a/b.jpg"]
         * 这种值如果要存储到封面对应的images字段中时，需要转化为逗号分割的方式，分割之前注意把域名前缀去掉，
         格式: /a/b.jpg, /a/b.jpg, /a/b.jpg
         */
        List<String> images = dto.getImages();

        if (images != null && images.size() > 0) {
            //遍历dto中封面图片地址,去除图片的域名和空格
            List<String> imageList =
                    images.stream().map(image -> image
                            .replace(webSite, "")
                            .replace(" ", ""))
                            .collect(Collectors.toList());

            //将列表转换为逗号分割的方式
            String imagesStr = StringUtils.join(imageList, ",");
            wmNews.setImages(imagesStr);
        }

        //2.4 操作保存或者更新文章 ↓↓↓ 1
        saveOrUpdateWmNews(wmNews, isSubmit);

        //抽取正文中的图片 ↓↓↓ 2
        List<String> contentImageList = extractContentImageList(dto.getContent());
        //todo 处理正文中图片路径带前缀问题

        //3.第三部分：维护文章 正文 中图片素材与文章的关系 ↓↓↓ 3
        if (isSubmit.equals(WmNews.Status.SUBMIT.getCode()) && contentImageList.size() > 0) {
            ResponseResult responseResult = saveRelativeForContent(contentImageList, wmNews.getId());
            if (responseResult != null) {
                return responseResult;
            }
        }

        //4.第四部分：维护文章 封面 中图片素材与文章的关系 ↓↓↓ 4
        if (isSubmit.equals(WmNews.Status.SUBMIT.getCode())) {
            ResponseResult responseResult = saveRelativeForCover(contentImageList, wmNews, dto);
            if (responseResult != null) {
                return responseResult;
            }
        }

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 查询文章详情
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult findOne(Integer id) {
        //1.检查参数
        if (id == null || id == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.判断用户是否登录
        if (WmThreadLocalUtils.getUser() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        //3.执行查询
        WmNews wmNews = getById(id);
        ResponseResult responseResult = ResponseResult.okResult(wmNews);
        responseResult.setHost(webSite); //设置域名前缀，将素材数据回显到页面上

        return responseResult;
    }

    /**
     * 删除文章
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult delOne(Integer id) {
        //1.检查参数
        if (id == null || id == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2.判断用户是否登录
        if (WmThreadLocalUtils.getUser() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        //3.查询数据判断是否存在
        WmNews wmNews = getById(id);
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }

        //4.判断文章的状态是否能进行删除
        // 状态为已发布并且已上架的文章不能删除
        if (wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode()) && wmNews.getEnable().equals(WemediaConstants.WM_NEWS_ENABLE_UP)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "文章已发布已上架不能删除");
        }

        //5.删除关联关系
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, id));

        //6.删除自媒体文章
        removeById(id);

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 文章上架 下架
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {
        //1.检查参数
        if (dto == null || dto.getId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        //2.判断用户是否登录
        if (WmThreadLocalUtils.getUser() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }

        //3.查询数据是否存在
        WmNews wmNews = getById(dto.getId());
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "文章不存在");
        }

        //4.修改文章状态，同步到app端（后期做）TODO
        //执行更新
        update(Wrappers.<WmNews>lambdaUpdate().eq(WmNews::getId, dto.getId()).set(WmNews::getEnable, dto.getEnable()));

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }



    //1.保存或者更新文章
    private void saveOrUpdateWmNews(WmNews wmNews, Short isSubmit) {
        wmNews.setUserId(WmThreadLocalUtils.getUser().getId()); //设置用户ID
        wmNews.setCreatedTime(new Date()); //创建时间
        wmNews.setSubmitedTime(new Date()); //提交时间
        wmNews.setEnable((short) 1); //设置为上架状态
        wmNews.setStatus(isSubmit); //保存草稿或提交审核

        //如果ID有值 应该修改文章
        if (wmNews.getId() != null && wmNews.getId() > 0) {
            //删除文章和素材的关系
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId, wmNews.getId()));
            //根据ID更新文章
            updateById(wmNews);

        } else {

            //如果ID无值应该保存文章
            save(wmNews);
        }
    }

    //2.抽取文章正文中的图片
    private List<String> extractContentImageList(String content) {
        List<String> contentImageList = new ArrayList<>();

        if (StringUtils.isNotBlank(content)) {

            //JSON格式转换成list (content 是String 但是内部是JSON数组)
            List<Map> mapList = JSON.parseArray(content, Map.class);

            for (Map<String, String> map : mapList) {
                //只取类型为图片的
                if ("image".equals(map.get("type"))) {
                    String contentImage = map.get("value");
                    //去除前缀之后的图片域名和空格
                    contentImage = contentImage.replace(webSite, "").replace(" ", "");
                    contentImageList.add(contentImage);
                }
            }
        }
        return contentImageList;
    }

    //3.保存 正文 中图片素材与文章的关系
    private ResponseResult saveRelativeForContent(List<String> contentImageList, Integer newsId) {
        ////保存关系为 正文引用 ↓↓↓ 5
        ResponseResult responseResult = saveRelativeInfo(contentImageList, newsId, WemediaConstants.WM_CONTENT_REFERENCE);

        if (responseResult != null) {
            return responseResult;
        }
        return null;
    }

    //4.保存 封面 中图片素材与文章的关系
    private ResponseResult saveRelativeForCover(List<String> contentImageList, WmNews wmNews, WmNewsDto dto) {
        List<String> images = dto.getImages(); //封面图片列表

        //如果是自动布局，那么封面的图片从文章正文中取
        if (dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
            int size = contentImageList.size();
            if (size >= 3) {
                //如果正文图片大于3张,取3张
                images = contentImageList.stream().limit(3).collect(Collectors.toList());
                //设置布局方式为多图布局
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);

            } else if (size >= 1 && size < 3) {
                //如果正文图片大于等于1小于3张，取1张图片
                images = contentImageList.stream().limit(1).collect(Collectors.toList());
                //设置布局方式为单图布局
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);

            } else {
                //无图布局
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }
            //如果封面图片内有值
            if (images != null && images.size() > 0) {
                wmNews.setImages(StringUtils.join(images, ","));
                updateById(wmNews);//更新文章
            }
        }

        //如果不是自动布局
        if (images != null && images.size() > 0) {
            //需要替换掉域名
            images =
                    images.stream().map(image -> image
                            .replace(webSite, "")
                            .replace(" ", ""))
                            .collect(Collectors.toList());
            //保存关系为 封面引用
            ResponseResult responseResult = saveRelativeInfo(images, wmNews.getId(), WemediaConstants.WM_COVER_REFERENCE);
            if (responseResult != null) {
                return responseResult;
            }
        }
        return null;
    }

    //5.公共方法：保存素材与文章的关系
    private ResponseResult saveRelativeInfo(List<String> materialList, Integer newsId, Short type) {
        //1.根据素材路径查询到素材ID值，将图片路径当做查询条件，实际就是in查询
        // select * from wm_material where url in ("/a/b/c.jpg", "/b/c/d.jpg")
        List<WmMaterial> imageList = wmMaterialMapper.selectList(Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, materialList));

        if (imageList == null || imageList.size() == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "素材不存在");
        }

        //2.处理查询条件中，哪些条件没有查到值，如果没查找直接响应错误
        // 实现逻辑：
        // 将查询的结果转为map，map的key是素材url，map的value是素材id，
        // {"/material/a.jpg":1 , "/material/b.jpg":2,"/material/c.jpg":3,...}
        // 然后遍历imageList，将imageList的遍历的元素当做map的key从map查数据
        Map<String, Integer> imageMap = imageList.stream().collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));
        List<Integer> materialIdList = new ArrayList<>(); //素材ID集合
        for (String imageUrl : materialList) {
            Integer imageId = imageMap.get(imageUrl);
            if (imageId == null) {
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "素材不存在");
            }
            materialIdList.add(imageId); //将素材ID添加到素材列表中
        }

        // 调用mapper 保存图片素材与文章的关系
        wmNewsMaterialMapper.saveRelationsByContent(materialIdList, newsId, type);
        return null;
    }

}